﻿namespace Gamelight.Graphics
{
	internal class LineDrawer
	{
		public Point[] GeneratePointListForThickFlatLine(int ax, int ay, int bx, int by, int width, bool roundCaps)
		{
			double angle = System.Math.Atan2(by - ay, bx - ax);
			double halfWidth = width / 2.0;

			if (roundCaps)
			{
				double twoPi = 3.14159265358979323 * 2;
				int numPoints = width * 2;
				int halfway = numPoints / 2;
				Point[] output = new Point[numPoints];
				int cx = 0;
				int cy = 0;
				angle += 3.14159265358979323 / 2;
				for (int i = 0; i < numPoints; ++i)
				{
					if (i < halfway)
					{
						cx = ax;
						cy = ay;
					}
					else
					{
						cx = bx;
						cy = by;
					}
					output[i] = new Point(
						cx + halfWidth * System.Math.Cos(angle + i * twoPi / numPoints),
						cy + halfWidth * System.Math.Sin(angle + i * twoPi / numPoints));
				}
				return output;
			}
			else
			{
				double piOverTwo = 3.14159265358979323 / 2;

				return new Point[] {
					new Point(ax + halfWidth * System.Math.Cos(angle + piOverTwo), ay + halfWidth * System.Math.Sin(angle + piOverTwo)),
					new Point(ax + halfWidth * System.Math.Cos(angle - piOverTwo), ay + halfWidth * System.Math.Sin(angle - piOverTwo)),
					new Point(bx + halfWidth * System.Math.Cos(angle - piOverTwo), by + halfWidth * System.Math.Sin(angle - piOverTwo)),
					new Point(bx + halfWidth * System.Math.Cos(angle + piOverTwo), by + halfWidth * System.Math.Sin(angle + piOverTwo)),
				};
			}
		}

		public void DrawPixelLine(
			int[,] pixels,
			int[] column_affected,
			int[] min_y_affected,
			int[] max_y_affected,
			int counter,
			int ax, int ay, int bx, int by)
		{
			int dx = bx - ax;
			int dy = by - ay;
			int x;

			if (dx == 0 && dy == 0)
			{
				// single point
				pixels[ax, ay] = counter;
				column_affected[ax] = counter;
				min_y_affected[ax] = ay;
				max_y_affected[ax] = ay;
			}
			else if (dx == 0)
			{
				// vertical line. 
				int start = ay < by ? ay : by;
				int end = ay < by ? by : ay;
				start = start < 0 ? 0 : start;
				column_affected[ax] = counter;
				min_y_affected[ax] = start;
				max_y_affected[ax] = end;
				for (int y = start; y <= end; ++y)
				{
					pixels[ax, y] = counter;
				}
			}
			else if (dy == 0)
			{
				// horizontal line.
				int start = ax < bx ? ax : bx;
				int end = ax < bx ? bx : ax;
				start = start < 0 ? 0 : start;
				for (x = start; x <= end; ++x)
				{
					column_affected[x] = counter;
					min_y_affected[x] = ay;
					max_y_affected[x] = ay;
					pixels[x, ay] = counter;
				}

			}
			else if (dx * dx >= dy * dy)
			{
				// line is more horizontal than vertical
				int left_x, right_x, left_y, right_y;

				if (ax < bx)
				{
					left_x = ax;
					left_y = ay;
					right_x = bx;
					right_y = by;
				}
				else
				{
					left_x = bx;
					left_y = by;
					right_x = ax;
					right_y = ay;
				}

				int start = left_x < 0 ? 0 : left_x;
				int y, t;
				int span = right_x - left_x;

				for (x = start; x <= right_x; ++x)
				{
					t = x - left_x;
					y = ((span - t) * left_y + t * right_y + (span >> 1)) / span;
					column_affected[x] = counter;
					min_y_affected[x] = y;
					max_y_affected[x] = y;
					pixels[x, y] = counter;
				}
			}
			else
			{
				int top_x, bottom_x, top_y, bottom_y;

				if (ay < by)
				{
					top_x = ax;
					top_y = ay;
					bottom_x = bx;
					bottom_y = by;
				}
				else
				{
					top_x = bx;
					top_y = by;
					bottom_x = ax;
					bottom_y = ay;
				}

				int start = top_y < 0 ? 0 : top_y;
				int y, t;
				int span = bottom_y - top_y;

				for (y = start; y <= bottom_y; ++y)
				{
					t = y - top_y;
					x = ((span - t) * top_x + t * bottom_x + (span >> 1)) / span;
					
					pixels[x, y] = counter;
					if (column_affected[x] != counter)
					{
						min_y_affected[x] = y;
						max_y_affected[x] = y;
						column_affected[x] = counter;
					}
					else
					{
						min_y_affected[x] = y < min_y_affected[x] ? y : min_y_affected[x];
						max_y_affected[x] = y > max_y_affected[x] ? y : max_y_affected[x];
					}
				}
			}
		}
	}
}
